package com.qfoud.edu.shiro.framwork.listener;

import com.qfoud.edu.framwork.annotation.Controller;
import com.qfoud.edu.framwork.annotation.RequestMapping;
import com.qfoud.edu.shiro.framwork.annotation.HasPerms;
import com.qfoud.edu.shiro.framwork.annotation.HasRoles;
import com.qfoud.edu.shiro.framwork.annotation.LoginResult;
import com.qfoud.edu.shiro.framwork.annotation.UserDetailService;
import com.qfoud.edu.shiro.framwork.exception.ShiroException;
import com.qfoud.edu.shiro.framwork.pojo.Context;
import com.qfoud.edu.shiro.framwork.result.LoginResultNotify;
import static com.qfoud.edu.shiro.framwork.pojo.Context.*;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;

/**
 * @author xiaobobo
 * @title: ApplicationListener
 * @projectName cd-java-2303-project-parent
 * @description: TODO
 * @date 2023/5/11  15:41
 */
@WebListener
public class ApplicationListener implements ServletContextListener {

    private List<File> fileList=new ArrayList<File>();

    public void contextInitialized(ServletContextEvent sce) {
        //找到MyUserDetailService这个类
        Executors.newSingleThreadExecutor().execute(
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            initUserDetailService(sce);
                            initLoginResult(sce);
                            initRolesAndPerms(sce);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        );
    }

    /**
     * 初始化我们的角色和权限的注解
     * @param sce
     */
    private void initRolesAndPerms(ServletContextEvent sce) {
        //TODO ：找到哪些方法拥有 HasRoles和HasPerms注解
        //TODO ：将请求地址作为key   权限或者角色值 作为值 放到map中去
        this.fileList=new ArrayList<>();
        //获取所有的class文件
        List<Class> classList = initResources(sce);
        //找这些class中是否有@HasPerms和HasRoles注解
        findOnConditionClassHasRolesAndPerms(classList);
    }

    /**
     * 找这些class中是否有HasRoles和HasPerms注解
     * @param classList
     */
    private void findOnConditionClassHasRolesAndPerms(List<Class> classList) {
        //遍历我们的集合
        for (int i = 0; i <classList.size() ; i++) {
            Class clazz = classList.get(i);
            //这里有个注意事项?这个@HasRoles和@HasPerms注解只能用在控制器中
            Annotation annotation = clazz.getAnnotation(Controller.class);
            if(null!=annotation){
                //这里获取类上面的@RequestMapping注解
                RequestMapping requestMapping1 = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                String typePath="";
                if(null!=requestMapping1){
                    typePath=requestMapping1.value();
                }
                //先获取这个类中所有的方法
                Method[] methods = clazz.getMethods();
                //遍历我们的方法
                for (Method method:methods) {
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                    String methodPath="";
                    if(null!=requestMapping){
                        methodPath = requestMapping.value();
                    }
                    //在这里就可以构建我们的key了
                    String key=(typePath+methodPath).trim();
                    if(null!=requestMapping){
                        //判定这个方法上是否拥有某一个注解?
                        //申明一个集合
                        List<String> permsAndRoles=new ArrayList<>();
                        HasRoles hasRoles = method.getAnnotation(HasRoles.class);
                        HasPerms hasPerms = method.getAnnotation(HasPerms.class);
                        if(null!=hasRoles){
                            //说明有HasRoles注解
                            //拿到当前方法需要访问的这个角色集合
                            String[] roles = hasRoles.roles();
                            for (String role:roles) {
                                permsAndRoles.add("ROLE_"+role);
                            }
                        }
                        if(null!=hasPerms){
                            //说明有perms注解
                            //当前方法需要访问的这个权限的集合
                            String[] perms = hasPerms.perms();
                            for (String perm:perms) {
                                permsAndRoles.add(perm);
                            }
                        }
                        //执行到这里需要添加到一个集合中去
                        if(permsAndRoles.size()>0){
                            //TODO ：将地址为key  集合为值添加到Map集合中去
                            Context.getRolesAndPerms().put(key,permsAndRoles);
                        }
                    }
                }
            }
        }
    }

    /**
     * 这里就是用来初始化登陆结果的
     * @param sce
     */
    private void initLoginResult(ServletContextEvent sce) throws InstantiationException, IllegalAccessException {
        this.fileList=new ArrayList<>();
        //获取所有的class文件
        List<Class> classList = initResources(sce);
        //找到拥有LoginResult注解的这个类的实例
        findOnConditionClassHavingLoginResult(classList);
    }

    /**
     * 这里就是找拥有LoginResult注解的这个实例对象
      * @param classList
     */
    private void findOnConditionClassHavingLoginResult(List<Class> classList) throws IllegalAccessException, InstantiationException {
        for (int i = 0; i <classList.size() ; i++) {
            //获取这个类的clazz对象
            Class clazz = classList.get(i);
            //判断这个clazz是否拥有 LoginResult注解
            LoginResult loginResult = (LoginResult) clazz.getAnnotation(LoginResult.class);
            if(null==loginResult){
               continue;
            }
            //程序执行到这里 说明这个类拥有LoginResult注解
            Object instance = clazz.newInstance();
            //还要判断这个实例是不是 LoginResultNotify的实例
            if(instance instanceof LoginResultNotify){
                //说明是这个类的实例
                Context.setLoginResultNotify((LoginResultNotify) instance);
            }else{
                throw new ShiroException("@LoginResult所注解的类不是LoginResultNotify的子类");
            }
        }
    }

    /**
     * 这里就是用来初始化UserDetailService的地方
     * @param sce
     */
    private void initUserDetailService(ServletContextEvent sce) throws InstantiationException, IllegalAccessException {
        this.fileList=new ArrayList<>();
        //初始化资源
        List<Class> classFileList = initResources(sce);
        //找到这些class中是否有 @interceptor注解
        findOnConditionClassHavingUserDetailService(classFileList);
    }

    /**
     * 初始化资源
     * @param sce
     * @return
     */
    private List<Class> initResources(ServletContextEvent sce) {
        //这里就构成了所有类的基路径
        String path=sce.getServletContext().getRealPath("/")+"WEB-INF\\classes\\";
        //遍历所有的文件
        listAllFiles(path);
        //找到所有的class文件
        List<File> fileList1= listClassFiles();
        //找到这些class文件的class对象回来
        return listClass(fileList1,sce);
    }


    /**
     * 遍历所有的文件
     * @param absolutePath
     */
    private void listAllFiles(String absolutePath) {
        File file1 = new File(absolutePath);
        File[] files = file1.listFiles();
        if(null!=files&&files.length>0){
            for (File f:files) {
                if(f.isFile()){
                    fileList.add(f);
                }else if(f.isDirectory()){
                    //说明是一个文件夹
                    listAllFiles(f.getAbsolutePath());
                }
            }
        }

    }

    /**
     * 分离出这个文件中的所有的Class文件
     * @return
     */
    private List<File> listClassFiles() {
        List<File> fileList=new ArrayList<File>();
        for (File file:this.fileList) {
            String absolutePath = file.getAbsolutePath();
            if(absolutePath.endsWith(".class")){
                fileList.add(file);
            }
        }
        return fileList;
    }

    /**
     * 找到class文件的class对象然后放到集合中
     * @param fileList
     * @return
     */
    private static List<Class> listClass(List<File> fileList,ServletContextEvent sce) {
        //首先申明容器
        List<Class> classes=new ArrayList<>();
        //首先准备我们的部署的根路径
        String realPath = sce.getServletContext().getRealPath("/")+"WEB-INF\\classes\\";
        //中间经历无数个步骤
        for (File file:fileList) {
            //获取当前文件的绝对路径
            String absolutePath = file.getAbsolutePath();
            // G:/部署根路径/   com/efoud/aunt/commons/controller/UserController    .class
            String classPath = absolutePath.replace(realPath, "");
            String substring = classPath.substring(0, classPath.lastIndexOf(".class"));
            String classPath1 = substring.replace("\\", ".");
            try {
                Class<?> aClass = Class.forName(classPath1);
                classes.add(aClass);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return classes;
    }

    /**
     * 找到这些class中是否有Interceptor注解
     * @param listAllFiles
     */
    private void findOnConditionClassHavingUserDetailService(List<Class> listAllFiles) throws IllegalAccessException, InstantiationException {
        //遍历我们的这个集合
        for (int i = 0; i <listAllFiles.size() ; i++) {
            //然后找到拥有@UserDetailService的这个类
            Class clazz = listAllFiles.get(i);
            if(null!=clazz.getAnnotation(UserDetailService.class)){
               //说明有这个注解
                com.qfoud.edu.shiro.framwork.service.base.UserDetailService userDetailService = (com.qfoud.edu.shiro.framwork.service.base.UserDetailService) clazz.newInstance();
                //然后放到上下文的环境中
                setUserDetailService(userDetailService);
                System.out.println("成功的设置了UserDetailService这个实例");
            }
        }
    }
}
